﻿using System.Web.UI.Design;
using System.Web.UI.Design.WebControls;

namespace Triad.Controls
{
    #region Usings
    using System;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics.CodeAnalysis;
    using System.Security.Permissions;
    using System.Web;
    using System.Web.Caching;
    using System.Web.UI;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Xml;
    using System.Configuration;
    #endregion
    /// <summary>
    /// tfListView Control - dynamic web grid/list view.
    /// </summary>
    /// <remarks>
    /// Author: tim.fischer,   8/25/2008 - 2:56 PM
    /// Modified By: 
    /// </remarks>
    [DefaultProperty("Text")]
    [AspNetHostingPermission(SecurityAction.Demand, Level = AspNetHostingPermissionLevel.Minimal)]
    [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
    [ToolboxData("<{0}:tfListView runat=server></{0}:tfListView>")]
    public partial class tfListView : GridView, IPostBackDataHandler
    {
        private DataTable c_MyTable;

        public enum RowType
        {
            Unknown=0,
            ModifiedOriginalData=1,
            Selected=2,
            Updated=3,
            Deleted=4,
            Added=5,
            All=6
        }

        /// <summary>
        /// Gets or sets the Core Data Table.
        /// </summary>
        /// <value>The Datatable representing the grid data.</value>
        /// <remarks>
        /// Author: tim.fischer,   8/26/2008 - 9:07 AM
        /// Modified By: 
        /// </remarks>
        public DataTable tfDataTable { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="tfListView"/> class.
        /// </summary>
        /// <remarks>
        /// Author: tim.fischer,   5/19/2008 - 2:19 PM
        /// Modified By:
        /// </remarks>
        public tfListView()
        {
            LoadDefaultStyles();
        }

        /// <summary>
        /// Gets the <see cref="T:System.Web.UI.HtmlTextWriterTag"/> value for the <see cref="T:System.Web.UI.WebControls.GridView"/> control.
        /// </summary>
        /// <value></value>
        /// <returns>
        /// The <see cref="T:System.Web.UI.HtmlTextWriterTag"/> value for the <see cref="T:System.Web.UI.WebControls.GridView"/> control.
        /// </returns>
        /// <remarks>
        /// Author: tim.fischer,   8/26/2008 - 8:58 AM
        /// Modified By: 
        /// </remarks>
        protected override HtmlTextWriterTag TagKey
        {
            get { return HtmlTextWriterTag.Div; }
        }

        #region IPostBackDataHandler Members

        bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection)
        {
            return LoadPostData(postDataKey, postCollection);
        }

        void IPostBackDataHandler.RaisePostDataChangedEvent()
        {
            RaisePostDataChangedEvent();
        }

        #endregion

        /// <summary>
        /// Handles the SelectedRow event of the tfListView control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected static void OnSelectedRow(object sender, EventArgs e)
        {
            var gridview = (GridView) sender;
            gridview.SelectedIndex = -1;
        }

        /// <summary>
        /// Loads the default styles.
        /// </summary>
        /// <remarks>
        /// Author: tim.fischer,   5/19/2008 - 2:18 PM
        /// Modified By: 
        /// </remarks>
        private void LoadDefaultStyles()
        {
            //CSSTheme.HeaderClass = "tfHeader";
            //CSSTheme.FooterClass = "tfFooter";
            //CSSTheme.SelectorItemSingleClass = "sRSelU";
            //CSSTheme.SelectorItemMultiClass = "mRSelU";
            //CSSTheme.RowClass = "tfRow";
            //CSSTheme.AlternatingRowClass = "tfARow";
            //CSSTheme.SelectedRowClass = "tfSelRow";
            //CSSTheme.SearchClass = "tfSearch";
            //CSSTheme.EditRowClass = "tfREdit";
        }
            //CSSTheme.GroupByTableClass = "";
            //CSSTheme.GroupByTableItemClass = "";

        /// <summary>
        /// Accepts all changes to the datatable.
        /// </summary>
        /// <remarks>
        /// Author: tim.fischer,   5/19/2008 - 2:18 PM
        /// Modified By: 
        /// </remarks>
        public void AcceptChanges()
        {
            HttpContext.Current.Response.Clear();
            HttpContext.Current.Response.Write("true" + Environment.NewLine);
        }

        /// <summary>
        /// Gets all data.
        /// </summary>
        /// <returns></returns>
        public DataSourceView GetAllData()
        {
            return base.GetData();
        }

        /// <summary>
        /// On Removal of the data from the cache.
        /// </summary>
        /// <param name="key">The key for the cache.</param>
        /// <param name="val">The value in the cache.</param>
        /// <param name="reason">The reason for removal.</param>
        /// <remarks>
        /// Author: tim.fischer,   7/29/2008 - 10:15 PM
        /// Modified By: 
        /// </remarks>
        
        private static void OnRemove(string key, object val, CacheItemRemovedReason reason)
        {
            //create an instance of the callback delegate
            CacheItemRemovedCallback callBack = OnRemove;
            HttpContext.Current.Cache.Insert(key, val + "*", null, DateTime.Now.AddSeconds(5), Cache.NoSlidingExpiration,
                CacheItemPriority.Default, callBack);
        }

        /// <summary>
        /// My Get DataSource method - checks cache, or parses datasource.
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// Author: tim.fischer,   5/19/2008 - 2:18 PM
        /// Modified By: 
        /// </remarks>
        private object GetMyDataSource(bool useCache)
        {
            var sessID = HttpContext.Current.Session.SessionID + ClientID + "DS";

            if (DataSource == null && HttpContext.Current.Cache[sessID] == null)
            {
                return null;
            }
            //CacheItemRemovedCallback callBack = onRemove;

            if (!useCache && (HttpContext.Current.Cache[sessID] == null))
            {
                //create an instance of the callback delegate
                HttpContext.Current.Cache.Insert(sessID, DataSource, null,
                   DateTime.Now.AddMinutes(1), TimeSpan.Zero, CacheItemPriority.Default, null);
            }
            object dataSource = useCache ? HttpContext.Current.Cache[sessID] : DataSource;

            if(dataSource==null && !String.IsNullOrEmpty(DataSourceID))
            {
                object datasource = null;
                foreach (Control control in Parent.Controls)
                {
                    if (control.ID == DataSourceID)
                    {
                        datasource = control;
                    }
                }
                if (datasource != null)
                {
                    if (typeof (AccessDataSource) == datasource.GetType())
                    {
                        var accessDataSource = (AccessDataSource) datasource;
                        dataSource = accessDataSource.Select(new DataSourceSelectArguments());
                    }
                    else if (typeof (SqlDataSource) == datasource.GetType())
                    {
                        var sqlDataSource = (SqlDataSource) datasource;
                        dataSource = sqlDataSource.Select(new DataSourceSelectArguments());
                    }
                }
            }
            DataSource = dataSource;
            
            return dataSource;
        }

        private static DataTable AddCustomColumns(DataTable dt)
        {
            DataColumn edUI = new DataColumn("_tfUI")
                                  {
                                      DataType = Type.GetType("System.Int32"),
                                      AutoIncrement = true,
                                      AutoIncrementSeed = 1000
                                  };
            edUI.AutoIncrementSeed = 10;
            dt.Columns.Add(edUI);
            dt.Columns.Add("_tfType", Type.GetType("System.Int32"));
            return dt;
        }

        private void FillDT(DataTable dt, XmlNodeList xmlnl, RowType iType)
        {
            if (xmlnl != null)
                for (int i = 0; i < xmlnl.Count; i++)
                    {
                        var node = xmlnl[i];
                        var row = dt.NewRow();
                        foreach (DataColumn column in dt.Columns)
                        {
                            if (column.ColumnName.Equals("_tfUI")) continue;
                            if (node[column.ColumnName] != null)
                            {
                                row[column.ColumnName] = node[column.ColumnName].InnerText;
                            }
                            else
                            {
                                row[column.ColumnName] = DBNull.Value;
                            }
                        }
                        if (iType == RowType.Selected) // Set Selection Criteria
                        {
                            row["_tfAT"] = "10";
                            SelectedIndex = Convert.ToInt32(row["_tfRI"].ToString());
                        }
                        row["_tfType"] = iType;
                        dt.Rows.Add(row); //Add the row to the datatable
                    }
        }

        private void GetLVResponse()
        {
            XmlDocument doc = new XmlDocument();
            var xmlS = HttpContext.Current.Server.UrlDecode(HttpContext.Current.Request.Form.ToString());
            doc.LoadXml(xmlS);
            var xml = doc.ChildNodes[1].ChildNodes[0].ChildNodes[0].ChildNodes[0].InnerText;
            xml = xml.Replace("<![CDATA_BEGIN[", "<![CDATA[").Replace("]]END>", "]]>");
            XmlDocument xmlDoc = new XmlDocument();
            xmlDoc.LoadXml(xml);

            var ds = new DataSet();

            var table = ds.Tables.Add(xmlDoc.ChildNodes[1].Attributes["TableName"].InnerText);
            table = AddCustomColumns(table);
            // Create Remaining DataTable Columns based on XML Schema
            var columnsNodes = xmlDoc.SelectNodes("DataSet/Columns");
            if (columnsNodes != null)
            {
                foreach (XmlNode node in columnsNodes[0].ChildNodes)
                {
                    if (node != null)
                    {
                        table.Columns.Add(node["Name"].InnerText, Type.GetType(node["DataType"].InnerText));
                    }
                }
            }
            table.AcceptChanges(); // Write Schema to Dataset/DataTable.
 
            // Fill Datatable with XML Data
            if (SendAllRowsInCallback != true)
            {
                FillDT(table, xmlDoc.SelectNodes("DataSet/ModifiedOriginalData"), RowType.ModifiedOriginalData);
                FillDT(table, xmlDoc.SelectNodes("DataSet/Sel"), RowType.Selected);
                FillDT(table, xmlDoc.SelectNodes("DataSet/Modified"), RowType.Updated);
                FillDT(table, xmlDoc.SelectNodes("DataSet/Deleted"), RowType.Deleted);
                FillDT(table, xmlDoc.SelectNodes("DataSet/Added"), RowType.Added);
            }
            else
            {
                FillDT(table, xmlDoc.SelectNodes("DataSet/All"), RowType.All);
            }

            var evTarget = xmlDoc.SelectSingleNode("DataSet/eventTarget").InnerText;                            
            var eventArgs = new lvUpdateEventArgs(table.DataSet, evTarget, "");
            OnUpdate(eventArgs);

            HttpContext.Current.Response.Clear();
            if (String.IsNullOrEmpty(eventArgs.RetVal))
            {
                HttpContext.Current.Response.Write("true" + Environment.NewLine);
            }
            else
            {
                HttpContext.Current.Response.Write(eventArgs.RetVal);
                HttpContext.Current.Response.End();
            }
        }

        /// <summary>
        /// Handles the <see cref="E:System.Web.UI.Control.Load"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> object that contains event data.</param>
        /// <remarks>
        /// Author: tim.fischer,   5/19/2008 - 2:18 PM
        /// Modified By: 
        /// </remarks>
        protected override void OnLoad(EventArgs e)
        {
            if (HttpContext.Current.Request.Form.ToString().IndexOf("tflvResult") > 0)
            {
                GetLVResponse();
                return;
            }
            string ctrlname = Page.Request.Params.Get("__EVENTTARGET");
            if (!String.IsNullOrEmpty(ctrlname))
            {
                if (ctrlname.Equals(ID + "_exp"))
                {
                    CustomExport();
                }
            }
            //base.OnLoad(e);
        }

        private void SetupClientScripts()
        {
            var sTmp = "~/_fw/Triad.Controls/";
            if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["Triad.Controls"]))
            {
                sTmp = ConfigurationManager.AppSettings["Triad.Controls"];
            }
            var sTheme = "/_fw/theme/red";
            if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["Theme"]))
            {
                sTheme = ConfigurationManager.AppSettings["Theme"];
            }

            var cm = Page.ClientScript;
            if (!cm.IsClientScriptBlockRegistered(typeof(Page), "tfcore"))
            {
                cm.RegisterClientScriptBlock(typeof(Page), "tfcore", " ", true);
                Page.ClientScript.RegisterClientScriptResource(typeof(tfResources),
                                   tfResources.RljCORE);
            }
            HtmlLink cssLink;
            if (!cm.IsClientScriptIncludeRegistered("tflv"))
            {
                cm.RegisterClientScriptInclude(GetType(),"tflv", ResolveClientUrl(sTmp+"js/tflv.js"));
                cssLink = new HtmlLink
                {
                    Href = ResolveClientUrl(sTheme + "/tfLView.css"),
                    ID = "tflvCSS"
                };
                cssLink.Attributes.Add("rel", "stylesheet");
                cssLink.Attributes.Add("type", "text/css");
                Page.Header.Controls.Add(cssLink);
                
            }

            if (Page.Header.FindControl("tfcbCSS") != null) return;
            if (!cm.IsClientScriptIncludeRegistered("tfcb"))
            {
                cm.RegisterClientScriptInclude("tfcb", ResolveClientUrl(sTmp + "js/tfcb.js"));


                cssLink = new HtmlLink
                              {
                                  Href = ResolveClientUrl(sTheme + "/tfCombobox.css"),
                                  ID = "tfcbCSS"
                              };
                cssLink.Attributes.Add("rel", "stylesheet");
                cssLink.Attributes.Add("type", "text/css");
                Page.Header.Controls.Add(cssLink);
            }
        }


        /// <summary>
        /// Raises the <see cref="E:System.Web.UI.Control.PreRender"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.EventArgs"/> that contains the event data.</param>
        /// <remarks>
        /// Author: tim.fischer,   8/26/2008 - 9:11 AM
        /// Modified By: 
        /// </remarks>
        [SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", MessageId = "0#")]
        protected override void OnPreRender(EventArgs e)
        {
            SetupClientScripts();
            //base.OnPreRender(e);
        }

        /// <summary>
        /// Renders the HTML opening tag of the control to the specified writer. This method is used primarily by control developers.
        /// </summary>
        /// <param name="writer">A <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
        /// <remarks>
        /// Author: tim.fischer,   8/26/2008 - 9:11 AM
        /// Modified By: 
        /// </remarks>
        public override void RenderBeginTag(HtmlTextWriter writer)
        {
            //string sTmp = "~/Triad.Controls/";
            //if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["Triad.Controls"]))
            //{
            //    sTmp = ConfigurationManager.AppSettings["Triad.Controls"];
            //}

            //sTmp = "~/_fw/theme/red";
            //if (!String.IsNullOrEmpty(ConfigurationManager.AppSettings["Theme"]))
            //{
            //    sTmp = ConfigurationManager.AppSettings["Theme"];
            //}

            //var cssLink = new HtmlLink
            //                   {
            //                       Href = ResolveClientUrl(sTmp+"css/tfLView.css"),
            //                       ID = "tflvCSS"
            //                   };
            //cssLink.Attributes.Add("rel", "stylesheet");
            //cssLink.Attributes.Add("type", "text/css");
            //cssLink.RenderControl(writer);

            if (AllowEditTemplate) { CreateEditTemplate(writer); }
            writer.Write("<div id=\"{0}tfALL\" class=\"tfALL\" style=\"width:{1};\">", ID, GridWidth);
        }

        /// <summary>
        /// Renders the HTML closing tag of the control into the specified writer. This method is used primarily by control developers.
        /// </summary>
        /// <param name="writer">A <see cref="T:System.Web.UI.HtmlTextWriter"/> that represents the output stream to render HTML content on the client.</param>
        /// <remarks>
        /// Author: tim.fischer,   8/26/2008 - 9:11 AM
        /// Modified By: 
        /// </remarks>
        public override void RenderEndTag(HtmlTextWriter writer)
        {
            // This is the code that renders in a WARP!
            //if (tfDataSource != null)
            //{
                writer.Write("</div><script type='text/javascript'>//<![CDATA[" + c_NewLine + RegisterScript() + c_NewLine);
                writer.WriteLine("//]]>" + c_NewLine + "</script>");
            //}
            // This is the code that renders!
        }

        /// <summary>
        /// Outputs server control content to a provided <see cref="T:System.Web.UI.HtmlTextWriter"/> object and stores tracing information about the control if tracing is enabled.
        /// </summary>
        /// <param name="writer">The <see cref="T:System.Web.UI.HtmlTextWriter"/> object that receives the control content.</param>
        /// <remarks>
        /// Author: tim.fischer,   8/26/2008 - 9:11 AM
        /// Modified By:
        /// </remarks>
        public override void RenderControl(HtmlTextWriter writer)
        {
            if (IsDesignTime)
            {
                RenderDesignTimeCode(writer);
                return;
            }

            RenderBeginTag(writer);
            c_MyTable = ParseDataSource();
            if (!RenderOnClient)
            {
                if (c_MyTable == null && Columns.Count == 0)
                {
                    writer.Write("<b>No Data Found</b>");
                }
                else
                {
                    writer.Write(ServerRender(c_MyTable));
                }
            }
            else
                writer.Write(RenderClientSideCode());

           RenderEndTag(writer);

           //base.RenderControl(writer); // just added
        }

        /// <summary>
        /// Creates the control hierarchy that is used to render a composite data-bound control based on the values that are stored in view state.
        /// </summary>
        /// <remarks>
        /// Author: tim.fischer,   8/26/2008 - 9:11 AM
        /// Modified By: 
        /// </remarks>
        protected override void CreateChildControls()
        {
            Controls.Clear();
        }

        /// <exclude />
        protected virtual Boolean LoadPostData(String postDataKey, NameValueCollection postCollection)
        {
            return true;
        }

        /// <exclude />
        [SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")]
        protected virtual void RaisePostDataChangedEvent()
        {
            OnSelectedIndexChanged(EventArgs.Empty);
        }
    } // End Class
} //End Namespace